import os
import shutil
import sqlite3
import threading
import time
import json
import logging
import ftplib
from datetime import datetime
from flask import Flask, request, jsonify, render_template, redirect, url_for

app = Flask(__name__)
TASKS = {}
DB_PATH = 'file_monitor.db'
LOG_DIR = 'logs'

# 确保日志目录存在
os.makedirs(LOG_DIR, exist_ok=True)

# 配置日志
logging.basicConfig(
    filename=os.path.join(LOG_DIR, 'app.log'),
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# 初始化数据库
def init_db():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS tasks
                 (id INTEGER PRIMARY KEY AUTOINCREMENT,
                 name TEXT NOT NULL,
                 source_dir TEXT NOT NULL,
                 dest_type TEXT NOT NULL,
                 dest_path TEXT NOT NULL,
                 file_types TEXT,
                 scan_interval INTEGER NOT NULL,
                 max_retries INTEGER NOT NULL,
                 status TEXT NOT NULL,
                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
    
    c.execute('''CREATE TABLE IF NOT EXISTS transfers
                 (id INTEGER PRIMARY KEY AUTOINCREMENT,
                 task_id INTEGER NOT NULL,
                 file_path TEXT NOT NULL,
                 status TEXT NOT NULL,
                 attempts INTEGER NOT NULL,
                 error TEXT,
                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                 completed_at TIMESTAMP,
                 FOREIGN KEY (task_id) REFERENCES tasks (id))''')
    
    conn.commit()
    conn.close()

# 任务类
class FileMonitorTask:
    def __init__(self, task_id, name, source_dir, dest_type, dest_path, file_types, scan_interval, max_retries):
        self.task_id = task_id
        self.name = name
        self.source_dir = source_dir
        self.dest_type = dest_type
        self.dest_path = dest_path
        self.file_types = file_types.split(',') if file_types else []
        self.scan_interval = scan_interval
        self.max_retries = max_retries
        self.running = False
        self.thread = None
        self.last_scan = None
        
    def start(self):
        if not self.running:
            self.running = True
            self.thread = threading.Thread(target=self._monitor_loop)
            self.thread.daemon = True
            self.thread.start()
            logging.info(f"Task {self.task_id} started")
            
    def stop(self):
        self.running = False
        if self.thread:
            self.thread.join(timeout=5)
        logging.info(f"Task {self.task_id} stopped")
            
    def _monitor_loop(self):
        while self.running:
            try:
                self._scan_directory()
                self.last_scan = datetime.now()
                time.sleep(self.scan_interval)
            except Exception as e:
                logging.error(f"Error in task {self.task_id}: {str(e)}")
                time.sleep(self.scan_interval)
    
    def _scan_directory(self):
        if not os.path.exists(self.source_dir):
            logging.error(f"Source directory {self.source_dir} does not exist")
            return
            
        for root, _, files in os.walk(self.source_dir):
            for file in files:
                if not self.file_types or any(file.endswith(ext) for ext in self.file_types):
                    file_path = os.path.join(root, file)
                    self._process_file(file_path)
    
    def _process_file(self, file_path):
        conn = sqlite3.connect(DB_PATH)
        c = conn.cursor()
        
        # 检查文件是否已处理
        c.execute("SELECT id, status, attempts FROM transfers WHERE task_id = ? AND file_path = ? ORDER BY id DESC LIMIT 1", 
                 (self.task_id, file_path))
        result = c.fetchone()
        
        if result:
            transfer_id, status, attempts = result
            if status == 'success':
                return  # 文件已成功传输
            elif attempts >= self.max_retries:
                return  # 已达到最大重试次数
        
        # 创建新的传输记录或更新现有记录
        if not result:
            c.execute("INSERT INTO transfers (task_id, file_path, status, attempts) VALUES (?, ?, ?, ?)", 
                     (self.task_id, file_path, 'pending', 0))
            transfer_id = c.lastrowid
        else:
            transfer_id = result[0]
        
        conn.commit()
        
        # 尝试传输文件
        success = self._transfer_file(file_path)
        
        # 更新传输记录
        status = 'success' if success else 'failed'
        c.execute("UPDATE transfers SET status = ?, attempts = attempts + 1, completed_at = CURRENT_TIMESTAMP WHERE id = ?", 
                 (status, transfer_id))
        
        if not success:
            # 记录错误日志
            error_log_dir = os.path.join(LOG_DIR, f"task_{self.task_id}", datetime.now().strftime("%Y-%m-%d"))
            os.makedirs(error_log_dir, exist_ok=True)
            error_file = os.path.join(error_log_dir, f"{os.path.basename(file_path)}.error")
            with open(error_file, 'w') as f:
                f.write(f"Failed to transfer file: {file_path}\n")
        
        conn.commit()
        conn.close()
    
    def _transfer_file(self, file_path):
        try:
            if self.dest_type == 'local':
                # 创建目标目录（如果不存在）
                relative_path = os.path.relpath(file_path, self.source_dir)
                dest_file = os.path.join(self.dest_path, relative_path)
                os.makedirs(os.path.dirname(dest_file), exist_ok=True)
                
                # 复制文件
                shutil.copy2(file_path, dest_file)
                logging.info(f"Copied {file_path} to {dest_file}")
                return True
                
            elif self.dest_type == 'ftp':
                # 解析FTP连接信息
                ftp_info = json.loads(self.dest_path)
                host = ftp_info.get('host')
                port = ftp_info.get('port', 21)
                user = ftp_info.get('user', '')
                password = ftp_info.get('password', '')
                remote_dir = ftp_info.get('remote_dir', '')
                
                # 连接FTP服务器
                ftp = ftplib.FTP()
                ftp.connect(host, port)
                ftp.login(user, password)
                
                # 切换到目标目录
                if remote_dir:
                    ftp.cwd(remote_dir)
                
                # 上传文件
                with open(file_path, 'rb') as f:
                    ftp.storbinary(f'STOR {os.path.basename(file_path)}', f)
                
                ftp.quit()
                logging.info(f"Uploaded {file_path} to FTP server")
                return True
                
            else:
                logging.error(f"Unsupported destination type: {self.dest_type}")
                return False
                
        except Exception as e:
            logging.error(f"Error transferring file {file_path}: {str(e)}")
            return False

# 加载所有任务
def load_tasks():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT id, name, source_dir, dest_type, dest_path, file_types, scan_interval, max_retries, status FROM tasks")
    tasks = c.fetchall()
    conn.close()
    
    for task_data in tasks:
        task_id, name, source_dir, dest_type, dest_path, file_types, scan_interval, max_retries, status = task_data
        task = FileMonitorTask(task_id, name, source_dir, dest_type, dest_path, file_types, scan_interval, max_retries)
        TASKS[task_id] = task
        if status == 'running':
            task.start()

# 路由 - 主页
@app.route('/')
def index():
    return render_template('index.html')

# API - 获取所有任务
@app.route('/api/tasks', methods=['GET'])
def get_tasks():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT id, name, source_dir, dest_type, dest_path, file_types, scan_interval, max_retries, status, created_at FROM tasks")
    columns = [col[0] for col in c.description]
    tasks = [dict(zip(columns, row)) for row in c.fetchall()]
    conn.close()
    
    # 添加运行状态信息
    for task in tasks:
        task_id = task['id']
        if task_id in TASKS:
            task['is_running'] = TASKS[task_id].running
            task['last_scan'] = TASKS[task_id].last_scan.strftime("%Y-%m-%d %H:%M:%S") if TASKS[task_id].last_scan else None
    
    return jsonify(tasks)

# API - 创建任务
@app.route('/api/tasks', methods=['POST'])
def create_task():
    data = request.json
    
    name = data.get('name')
    source_dir = data.get('source_dir')
    dest_type = data.get('dest_type')
    dest_path = data.get('dest_path')
    file_types = data.get('file_types')
    scan_interval = data.get('scan_interval', 60)
    max_retries = data.get('max_retries', 3)
    status = data.get('status', 'stopped')
    
    if not name or not source_dir or not dest_type or not dest_path:
        return jsonify({'error': 'Missing required fields'}), 400
    
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("INSERT INTO tasks (name, source_dir, dest_type, dest_path, file_types, scan_interval, max_retries, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
             (name, source_dir, dest_type, dest_path, file_types, scan_interval, max_retries, status))
    task_id = c.lastrowid
    conn.commit()
    conn.close()
    
    # 创建并启动任务（如果状态为运行）
    task = FileMonitorTask(task_id, name, source_dir, dest_type, dest_path, file_types, scan_interval, max_retries)
    TASKS[task_id] = task
    if status == 'running':
        task.start()
    
    return jsonify({'id': task_id}), 201

# API - 获取任务详情
@app.route('/api/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("SELECT id, name, source_dir, dest_type, dest_path, file_types, scan_interval, max_retries, status, created_at FROM tasks WHERE id = ?", (task_id,))
    row = c.fetchone()
    conn.close()
    
    if not row:
        return jsonify({'error': 'Task not found'}), 404
    
    columns = [col[0] for col in c.description]
    task = dict(zip(columns, row))
    
    # 添加运行状态信息
    if task_id in TASKS:
        task['is_running'] = TASKS[task_id].running
        task['last_scan'] = TASKS[task_id].last_scan.strftime("%Y-%m-%d %H:%M:%S") if TASKS[task_id].last_scan else None
    
    return jsonify(task)

# API - 更新任务
@app.route('/api/tasks/<int:task_id>', methods=['PUT'])
def update_task(task_id):
    if task_id not in TASKS:
        return jsonify({'error': 'Task not found'}), 404
    
    data = request.json
    
    # 停止当前任务
    TASKS[task_id].stop()
    
    # 更新任务属性
    if 'name' in data:
        TASKS[task_id].name = data['name']
    if 'source_dir' in data:
        TASKS[task_id].source_dir = data['source_dir']
    if 'dest_type' in data:
        TASKS[task_id].dest_type = data['dest_type']
    if 'dest_path' in data:
        TASKS[task_id].dest_path = data['dest_path']
    if 'file_types' in data:
        TASKS[task_id].file_types = data['file_types'].split(',') if data['file_types'] else []
    if 'scan_interval' in data:
        TASKS[task_id].scan_interval = data['scan_interval']
    if 'max_retries' in data:
        TASKS[task_id].max_retries = data['max_retries']
    
    # 更新数据库
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("""UPDATE tasks 
                 SET name = ?, 
                     source_dir = ?, 
                     dest_type = ?, 
                     dest_path = ?, 
                     file_types = ?, 
                     scan_interval = ?, 
                     max_retries = ?, 
                     status = ? 
                 WHERE id = ?""",
             (TASKS[task_id].name, 
              TASKS[task_id].source_dir, 
              TASKS[task_id].dest_type, 
              TASKS[task_id].dest_path, 
              ','.join(TASKS[task_id].file_types) if TASKS[task_id].file_types else '',
              TASKS[task_id].scan_interval, 
              TASKS[task_id].max_retries, 
              'running' if data.get('status') == 'running' else 'stopped',
              task_id))
    conn.commit()
    conn.close()
    
    # 启动任务（如果需要）
    if data.get('status') == 'running':
        TASKS[task_id].start()
    
    return jsonify({'success': True})

# API - 删除任务
@app.route('/api/tasks/<int:task_id>', methods=['DELETE'])
def delete_task(task_id):
    if task_id in TASKS:
        TASKS[task_id].stop()
        del TASKS[task_id]
    
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("DELETE FROM tasks WHERE id = ?", (task_id,))
    c.execute("DELETE FROM transfers WHERE task_id = ?", (task_id,))
    conn.commit()
    conn.close()
    
    return jsonify({'success': True})

# API - 启动任务
@app.route('/api/tasks/<int:task_id>/start', methods=['POST'])
def start_task(task_id):
    if task_id not in TASKS:
        return jsonify({'error': 'Task not found'}), 404
    
    TASKS[task_id].start()
    
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("UPDATE tasks SET status = 'running' WHERE id = ?", (task_id,))
    conn.commit()
    conn.close()
    
    return jsonify({'success': True})

# API - 停止任务
@app.route('/api/tasks/<int:task_id>/stop', methods=['POST'])
def stop_task(task_id):
    if task_id not in TASKS:
        return jsonify({'error': 'Task not found'}), 404
    
    TASKS[task_id].stop()
    
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("UPDATE tasks SET status = 'stopped' WHERE id = ?", (task_id,))
    conn.commit()
    conn.close()
    
    return jsonify({'success': True})

# API - 获取任务统计信息
@app.route('/api/tasks/<int:task_id>/stats', methods=['GET'])
def get_task_stats(task_id):
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    
    # 总文件数
    c.execute("SELECT COUNT(*) FROM transfers WHERE task_id = ?", (task_id,))
    total_files = c.fetchone()[0]
    
    # 成功文件数
    c.execute("SELECT COUNT(*) FROM transfers WHERE task_id = ? AND status = 'success'", (task_id,))
    success_files = c.fetchone()[0]
    
    # 失败文件数
    c.execute("SELECT COUNT(*) FROM transfers WHERE task_id = ? AND status = 'failed'", (task_id,))
    failed_files = c.fetchone()[0]
    
    # 最近传输记录
    c.execute("SELECT file_path, status, attempts, error, created_at, completed_at FROM transfers WHERE task_id = ? ORDER BY id DESC LIMIT 10", (task_id,))
    columns = [col[0] for col in c.description]
    recent_transfers = [dict(zip(columns, row)) for row in c.fetchall()]
    
    conn.close()
    
    stats = {
        'total_files': total_files,
        'success_files': success_files,
        'failed_files': failed_files,
        'success_rate': (success_files / total_files) * 100 if total_files > 0 else 0,
        'recent_transfers': recent_transfers
    }
    
    return jsonify(stats)

# API - 获取所有任务统计信息
@app.route('/api/stats', methods=['GET'])
def get_all_stats():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    
    # 总任务数
    c.execute("SELECT COUNT(*) FROM tasks")
    total_tasks = c.fetchone()[0]
    
    # 运行中任务数
    c.execute("SELECT COUNT(*) FROM tasks WHERE status = 'running'")
    running_tasks = c.fetchone()[0]
    
    # 已停止任务数
    c.execute("SELECT COUNT(*) FROM tasks WHERE status = 'stopped'")
    stopped_tasks = c.fetchone()[0]
    
    # 总文件数
    c.execute("SELECT COUNT(*) FROM transfers")
    total_files = c.fetchone()[0]
    
    # 成功文件数
    c.execute("SELECT COUNT(*) FROM transfers WHERE status = 'success'")
    success_files = c.fetchone()[0]
    
    # 失败文件数
    c.execute("SELECT COUNT(*) FROM transfers WHERE status = 'failed'")
    failed_files = c.fetchone()[0]
    
    conn.close()
    
    stats = {
        'total_tasks': total_tasks,
        'running_tasks': running_tasks,
        'stopped_tasks': stopped_tasks,
        'total_files': total_files,
        'success_files': success_files,
        'failed_files': failed_files,
        'success_rate': (success_files / total_files) * 100 if total_files > 0 else 0
    }
    
    return jsonify(stats)

if __name__ == '__main__':
    init_db()
    load_tasks()
    app.run(debug=True)    